#include <unistd.h>
#include <getopt.h>
#include <sqlite3.h>

#include <string>
#include <memory>
#include <vector>
#include <utility>

#include "sym-name-table.h"
#include "database.h"
#include "string-util.h"
#include "autocall-util.h"

using std::vector;
using std::string;
using std::shared_ptr;
using std::dynamic_pointer_cast;
using std::runtime_error;

struct CSymName : public SymName {
    friend class CSymNameTable;

    const std::string& language() override;
    const std::string& name() override;

    static const string LANGUAGE;
private:
    string sym_name_;
};

const string CSymName::LANGUAGE {"C"};

const std::string& CSymName::language()
{
    return CSymName::LANGUAGE;
}

const std::string& CSymName::name()
{
    return sym_name_;
}


struct CSymNameTable : public SymNameTable {
    const std::string& help() override;

    const std::string& language() override;

    std::shared_ptr<SymName> getSymName(int argc,char **argv) override;

    std::vector<std::pair<SymInfoTable::id_t,std::shared_ptr<SymName>>>
    find(std::shared_ptr<SymName>) override ;

    void add(SymInfoTable::id_t,std::shared_ptr<SymName>) override;

    // 在构造函数中完成表的创建.
    CSymNameTable();

private:
    static const char *table_name;
    static const string help_doc;
};

const string CSymNameTable::help_doc {"符号名必须放在命令行参数中最后一个非选项参数!"};
const char* CSymNameTable::table_name = "c_sym_name_table";

const std::string& CSymNameTable::help()
{
    return help_doc;
}

const std::string& CSymNameTable::language()
{
    return CSymName::LANGUAGE;
}

CSymNameTable::CSymNameTable()
{
    auto conn_ptr = database_connection();
    string sql_stmt_str = format(
        "CREATE TABLE IF NOT EXISTS  %s ("
        "sym_info_id INT UNIQUE NOT NULL,"
        "sym_name BLOB NOT NULL UNIQUE)",table_name);
    exec_sql(conn_ptr,sql_stmt_str.c_str());
    return ;
}

std::shared_ptr<SymName>
CSymNameTable::getSymName(int argc,char **argv)
{
    const char *sym_name = NULL;

    int getopt_ret;
    optind = 0;
    opterr = 0;
    while ((getopt_ret = getopt(argc,argv,"-")) != -1) {
        switch (getopt_ret) {
        case 1:
            sym_name = optarg;
            break;
        default:
            break;
        }
    }
    if (sym_name == NULL)
        throw runtime_error("C-LANGUAGE: 未指定符号名!");

    CSymName *c_sym_name = new CSymName;
    c_sym_name->sym_name_ = sym_name;
    return shared_ptr<SymName>{c_sym_name};
}

std::vector<std::pair<SymInfoTable::id_t,std::shared_ptr<SymName>>>
CSymNameTable::find(std::shared_ptr<SymName> s)
{
    auto c_symbol_ptr = dynamic_pointer_cast<CSymName>(s);
    if (!c_symbol_ptr)
        throw runtime_error("C-LANGUAGES.find(): 参数不合法!");
    string c_symbol_name = c_symbol_ptr->sym_name_;

    // 构建并 prepare sql 语句.
    string sql_stmt_str = format(
                "SELECT sym_info_id FROM %s WHERE sym_name = '%s'",
                table_name,c_symbol_name.c_str());
    sqlite3_stmt *sql_stmt_ptr = NULL;
    auto conn_ptr = database_connection();
    int prepare_ret = sqlite3_prepare(conn_ptr,sql_stmt_str.c_str(),-1,&sql_stmt_ptr,NULL);
    if (prepare_ret != SQLITE_OK) {
        throw runtime_error(format("sql语句 %s prepare 出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(conn_ptr)));
    }
    AUTOCALL_FUNC(destroy_prepare_stmt) {
        if (sql_stmt_ptr != NULL)
            sqlite3_finalize(sql_stmt_ptr);
    };
    AUTOCALL(destroy_prepare_stmt);

    // 执行 sql 语句,并收获结果.
    int step_ret = sqlite3_step(sql_stmt_ptr);
    if (step_ret != SQLITE_ROW && step_ret != SQLITE_DONE) {
        throw runtime_error(format("sql语句 %s step 出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(conn_ptr)));
    }
    std::vector<std::pair<SymInfoTable::id_t,std::shared_ptr<SymName>>> ret_vec;
    if (step_ret != SQLITE_DONE) {
        sqlite3_int64 sym_info_id = sqlite3_column_int64(sql_stmt_ptr,0);

        std::pair<SymInfoTable::id_t,std::shared_ptr<SymName>> res_pair;
        res_pair.first = sym_info_id;
        res_pair.second = s;
        ret_vec.push_back(res_pair);
    }
    return ret_vec;
}

void CSymNameTable::add(SymInfoTable::id_t id,std::shared_ptr<SymName> s)
{
    auto c_symbol_ptr = dynamic_pointer_cast<CSymName>(s);
    if (!c_symbol_ptr)
        throw runtime_error("C-LANGUAGES.add(): 参数不合法!");
    string c_symbol_name = c_symbol_ptr->sym_name_;

    string sql_stmt_str = format("INSERT INTO  %s (sym_info_id,sym_name) "
        "VALUES (%lld,'%s')",table_name,id,c_symbol_name.c_str());
    auto conn_ptr = database_connection();
    exec_sql(conn_ptr,sql_stmt_str.c_str());
    return ;
}

LANGUAGE_REGISTER(CSymName::LANGUAGE,CSymNameTable);
